/*
 * abstraction of the spi interface of HopeRf rf69 radio module
 *
 * Copyright (C) 2016 Wolf-Entwicklungen
 *	Marcus Wolf <linux@wolf-entwicklungen.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/* enable prosa debug info */
#undef DEBUG
/* enable print of values on reg access */
#undef DEBUG_VALUES
/* enable print of values on fifo access */
#undef DEBUG_FIFO_ACCESS

#include <linux/types.h>
#include <linux/spi/spi.h>

#include "rf69.h"
#include "rf69_registers.h"

#define F_OSC	  32000000 /* in Hz */
#define FIFO_SIZE 66 	   /* in byte */

/*-------------------------------------------------------------------------*/

#define READ_REG(x)	rf69_read_reg (spi, x)
#define WRITE_REG(x,y)	rf69_write_reg(spi, x, y)

/*-------------------------------------------------------------------------*/

int rf69_set_mode(struct spi_device *spi, enum mode mode)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: mode");
	#endif

	switch (mode) {
	case transmit:	  return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_TRANSMIT);
	case receive:	  return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_RECEIVE);
	case synthesizer: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_SYNTHESIZER);
	case standby:	  return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_STANDBY);
	case mode_sleep:  return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_SLEEP);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// we are using packet mode, so this check is not really needed
	// but waiting for mode ready is necessary when going from sleep because the FIFO may not be immediately available from previous mode
	//while (_mode == RF69_MODE_SLEEP && (READ_REG(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // Wait for ModeReady

}

int rf69_set_data_mode(struct spi_device *spi, enum dataMode dataMode)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: data mode");
	#endif

	switch (dataMode) {
	case packet:		return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_PACKET);
	case continuous:	return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_CONTINUOUS);
	case continuousNoSync:  return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_CONTINUOUS_NOSYNC);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_modulation(struct spi_device *spi, enum modulation modulation)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: modulation");
	#endif

	switch (modulation) {
	case OOK:   return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_TYPE) | DATAMODUL_MODULATION_TYPE_OOK);
	case FSK:   return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_TYPE) | DATAMODUL_MODULATION_TYPE_FSK);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

enum modulation rf69_get_modulation(struct spi_device *spi)
{
	u8 currentValue;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "get: mode");
	#endif

	currentValue = READ_REG(REG_DATAMODUL);

	switch (currentValue & MASK_DATAMODUL_MODULATION_TYPE >> 3) { // TODO improvement: change 3 to define
	case DATAMODUL_MODULATION_TYPE_OOK: return OOK;
	case DATAMODUL_MODULATION_TYPE_FSK: return FSK;
	default:			    return undefined;
	}
}

int rf69_set_modulation_shaping(struct spi_device *spi, enum modShaping modShaping)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: mod shaping");
	#endif

	if (rf69_get_modulation(spi) == FSK) {
		switch (modShaping) {
		case shapingOff: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_NONE);
		case shaping1_0: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_1_0);
		case shaping0_5: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_0_3);
		case shaping0_3: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_0_5);
		default:
			dev_dbg(&spi->dev, "set: illegal input param");
			return -EINVAL;
		}
	} else {
		switch (modShaping) {
		case shapingOff: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_NONE);
		case shapingBR:	 return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_BR);
		case shaping2BR: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_2BR);
		default:
			dev_dbg(&spi->dev, "set: illegal input param");
			return -EINVAL;
		}
	}
}

int rf69_set_bit_rate(struct spi_device *spi, u16 bitRate)
{
	int retval;
	u32 bitRate_min;
	u32 bitRate_reg;
	u8 msb;
	u8 lsb;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: bit rate");
	#endif

	// check input value
	bitRate_min = F_OSC / 8388608; // 8388608 = 2^23;
	if (bitRate < bitRate_min) {
		dev_dbg(&spi->dev, "setBitRate: illegal input param");
		return -EINVAL;
	}

	// calculate reg settings
	bitRate_reg = (F_OSC / bitRate);

	msb = (bitRate_reg&0xff00)   >>  8;
	lsb = (bitRate_reg&0xff);

	// transmit to RF 69
	retval = WRITE_REG(REG_BITRATE_MSB, msb);
	if (retval)  return retval;
	retval = WRITE_REG(REG_BITRATE_LSB, lsb);
	if (retval)  return retval;

	return 0;
}

int rf69_set_deviation(struct spi_device *spi, u32 deviation)
{
	int retval;
//	u32 f_max; TODO: Abh�ngigkeit von Bitrate beachten!!
	u64 f_reg;
	u64 f_step;
	u8 msb;
	u8 lsb;
	u64 factor = 1000000; // to improve precision of calculation

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: deviation");
	#endif

	if (deviation < 600 || deviation > 500000) { //TODO: Abh�ngigkeit von Bitrate beachten!!
		dev_dbg(&spi->dev, "set_deviation: illegal input param");
		return -EINVAL;
	}

	// calculat f step
	f_step = F_OSC * factor;
	do_div(f_step, 524288); //  524288 = 2^19

	// calculate register settings
	f_reg = deviation * factor;
	do_div(f_reg  , f_step);

	msb = (f_reg&0xff00)   >>  8;
	lsb = (f_reg&0xff);

	// check msb
	if (msb & ~FDEVMASB_MASK) {
		dev_dbg(&spi->dev, "set_deviation: err in calc of msb");
		return -EINVAL;
	}

	// write to chip
	retval = WRITE_REG(REG_FDEV_MSB, msb);
	if (retval)  return retval;
	retval = WRITE_REG(REG_FDEV_LSB, lsb);
	if (retval)  return retval;

	return 0;
}

int rf69_set_frequency(struct spi_device *spi, u32 frequency)
{
	int retval;
	u32 f_max;
	u64 f_reg;
	u64 f_step;
	u8 msb;
	u8 mid;
	u8 lsb;
	u64 factor = 1000000; // to improve precision of calculation

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: frequency");
	#endif

	// calculat f step
	f_step = F_OSC * factor;
	do_div(f_step, 524288); //  524288 = 2^19

	// check input value
	f_max = div_u64(f_step * 8388608, factor);
	if (frequency > f_max) {
		dev_dbg(&spi->dev, "setFrequency: illegal input param");
		return -EINVAL;
	}

	// calculate reg settings
	f_reg = frequency * factor;
	do_div(f_reg  , f_step);

	msb = (f_reg&0xff0000) >> 16;
	mid = (f_reg&0xff00)   >>  8;
	lsb = (f_reg&0xff);

	// write to chip
	retval = WRITE_REG(REG_FRF_MSB, msb);
	if (retval)  return retval;
	retval = WRITE_REG(REG_FRF_MID, mid);
	if (retval)  return retval;
	retval = WRITE_REG(REG_FRF_LSB, lsb);
	if (retval)  return retval;

	return 0;
}

int rf69_set_amplifier_0(struct spi_device *spi, enum optionOnOff optionOnOff)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: amp #0");
	#endif

	switch(optionOnOff) {
	case optionOn:	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) |  MASK_PALEVEL_PA0) );
	case optionOff:	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA0) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_amplifier_1(struct spi_device *spi, enum optionOnOff optionOnOff)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: amp #1");
	#endif

	switch(optionOnOff) {
	case optionOn:	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) |  MASK_PALEVEL_PA1) );
	case optionOff: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA1) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_amplifier_2(struct spi_device *spi, enum optionOnOff optionOnOff)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: amp #2");
	#endif

	switch(optionOnOff) {
	case optionOn:	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) |  MASK_PALEVEL_PA2) );
	case optionOff:	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA2) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_output_power_level(struct spi_device *spi, u8 powerLevel)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: power level");
	#endif

	powerLevel +=18; // TODO Abh�ngigkeit von PA0,1,2 setting

	// check input value
	if (powerLevel > 0x1f) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// write value
	return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_OUTPUT_POWER) | powerLevel);
}

int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: pa ramp");
	#endif

	switch(paRamp) {
	case ramp3400:	return WRITE_REG(REG_PARAMP, PARAMP_3400);
	case ramp2000:	return WRITE_REG(REG_PARAMP, PARAMP_2000);
	case ramp1000:	return WRITE_REG(REG_PARAMP, PARAMP_1000);
	case ramp500:	return WRITE_REG(REG_PARAMP, PARAMP_500);
	case ramp250:	return WRITE_REG(REG_PARAMP, PARAMP_250);
	case ramp125:	return WRITE_REG(REG_PARAMP, PARAMP_125);
	case ramp100:	return WRITE_REG(REG_PARAMP, PARAMP_100);
	case ramp62:	return WRITE_REG(REG_PARAMP, PARAMP_62);
	case ramp50:	return WRITE_REG(REG_PARAMP, PARAMP_50);
	case ramp40:	return WRITE_REG(REG_PARAMP, PARAMP_40);
	case ramp31:	return WRITE_REG(REG_PARAMP, PARAMP_31);
	case ramp25:	return WRITE_REG(REG_PARAMP, PARAMP_25);
	case ramp20:	return WRITE_REG(REG_PARAMP, PARAMP_20);
	case ramp15:	return WRITE_REG(REG_PARAMP, PARAMP_15);
	case ramp12:	return WRITE_REG(REG_PARAMP, PARAMP_12);
	case ramp10:	return WRITE_REG(REG_PARAMP, PARAMP_10);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance antennaImpedance)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: antenna impedance");
	#endif

	switch(antennaImpedance) {
	case fiftyOhm:	    return WRITE_REG(REG_LNA, (READ_REG(REG_LNA) & ~MASK_LNA_ZIN) );
	case twohundretOhm: return WRITE_REG(REG_LNA, (READ_REG(REG_LNA) |  MASK_LNA_ZIN) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: lna gain");
	#endif

	switch(lnaGain) {
	case automatic:	 return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_AUTO) );
	case max:	 return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX) );
	case maxMinus6:  return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_6) );
	case maxMinus12: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_12) );
	case maxMinus24: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_24) );
	case maxMinus36: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_36) );
	case maxMinus48: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_48) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

enum lnaGain rf69_get_lna_gain(struct spi_device *spi)
{
	u8 currentValue;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "get: lna gain");
	#endif

	currentValue = READ_REG(REG_LNA);

	switch (currentValue & MASK_LNA_CURRENT_GAIN >> 3) { // improvement: change 3 to define
	case LNA_GAIN_AUTO:	    return automatic;
	case LNA_GAIN_MAX:	    return max;
	case LNA_GAIN_MAX_MINUS_6:  return maxMinus6;
	case LNA_GAIN_MAX_MINUS_12: return maxMinus12;
	case LNA_GAIN_MAX_MINUS_24: return maxMinus24;
	case LNA_GAIN_MAX_MINUS_36: return maxMinus36;
	case LNA_GAIN_MAX_MINUS_48: return maxMinus48;
	default:		    return undefined;
	}
}

int rf69_set_dc_cut_off_frequency_intern(struct spi_device *spi ,u8 reg, enum dccPercent dccPercent)
{
	switch (dccPercent) {
	case dcc16Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_16_PERCENT) );
	case dcc8Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_8_PERCENT) );
	case dcc4Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_4_PERCENT) );
	case dcc2Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_2_PERCENT) );
	case dcc1Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_1_PERCENT) );
	case dcc0_5Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_5_PERCENT) );
	case dcc0_25Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_25_PERCENT) );
	case dcc0_125Percent:	return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_125_PERCENT) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_dc_cut_off_frequency(struct spi_device *spi, enum dccPercent dccPercent)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: cut off freq");
	#endif

	return rf69_set_dc_cut_off_frequency_intern(spi, REG_RXBW, dccPercent);
}

int rf69_set_dc_cut_off_frequency_during_afc(struct spi_device *spi, enum dccPercent dccPercent)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: cut off freq during afc");
	#endif

	return rf69_set_dc_cut_off_frequency_intern(spi, REG_AFCBW, dccPercent);
}

static int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg,
				     enum mantisse mantisse, u8 exponent)
{
	u8 newValue;

	// check value for mantisse and exponent
	if (exponent > 7) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	if ((mantisse != mantisse16) &&
	    (mantisse != mantisse20) &&
	    (mantisse != mantisse24)) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// read old value
	newValue = READ_REG(reg);

	// "delete" mantisse and exponent = just keep the DCC setting
	newValue = newValue & MASK_BW_DCC_FREQ;

	// add new mantisse
	switch(mantisse) {
	case mantisse16: newValue = newValue | BW_MANT_16;	break;
	case mantisse20: newValue = newValue | BW_MANT_20;	break;
	case mantisse24: newValue = newValue | BW_MANT_24;	break;
	}

	// add new exponent
	newValue = newValue | exponent;

	// write back
	return WRITE_REG(reg, newValue);
}

int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse, u8 exponent)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: band width");
	#endif

	return rf69_set_bandwidth_intern(spi, REG_RXBW, mantisse, exponent);
}

int rf69_set_bandwidth_during_afc(struct spi_device *spi, enum mantisse mantisse, u8 exponent)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: band width during afc");
	#endif

	return rf69_set_bandwidth_intern(spi, REG_AFCBW, mantisse, exponent);
}

int rf69_set_ook_threshold_type(struct spi_device *spi, enum thresholdType thresholdType)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: threshold type");
	#endif

	switch (thresholdType) {
	case fixed:	return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_FIXED) );
	case peak:	return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_PEAK) );
	case average:	return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_AVERAGE) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_ook_threshold_step(struct spi_device *spi, enum thresholdStep thresholdStep)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: threshold step");
	#endif

	switch (thresholdStep) {
	case step_0_5db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_0_5_DB) );
	case step_1_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_1_0_DB) );
	case step_1_5db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_1_5_DB) );
	case step_2_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_2_0_DB) );
	case step_3_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_3_0_DB) );
	case step_4_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_4_0_DB) );
	case step_5_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_5_0_DB) );
	case step_6_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_6_0_DB) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement thresholdDecrement)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: threshold decrement");
	#endif

	switch (thresholdDecrement) {
	case dec_every8th: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_8TH) );
	case dec_every4th: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_4TH) );
	case dec_every2nd: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_2ND) );
	case dec_once:	   return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_ONCE) );
	case dec_twice:	   return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_TWICE) );
	case dec_4times:   return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_4_TIMES) );
	case dec_8times:   return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_8_TIMES) );
	case dec_16times:  return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_16_TIMES) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value)
{
	u8 mask;
	u8 shift;
	u8 regaddr;
	u8 regValue;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: DIO mapping");
	#endif

	switch (DIONumber) {
	case 0: mask=MASK_DIO0; shift=SHIFT_DIO0; regaddr=REG_DIOMAPPING1; break;
	case 1: mask=MASK_DIO1; shift=SHIFT_DIO1; regaddr=REG_DIOMAPPING1; break;
	case 2: mask=MASK_DIO2; shift=SHIFT_DIO2; regaddr=REG_DIOMAPPING1; break;
	case 3: mask=MASK_DIO3; shift=SHIFT_DIO3; regaddr=REG_DIOMAPPING1; break;
	case 4: mask=MASK_DIO4; shift=SHIFT_DIO4; regaddr=REG_DIOMAPPING2; break;
	case 5: mask=MASK_DIO5; shift=SHIFT_DIO5; regaddr=REG_DIOMAPPING2; break;
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// read reg
	regValue=READ_REG(regaddr);
	// delete old value
	regValue = regValue & ~mask;
	// add new value
	regValue = regValue | value << shift;
	// write back
	return WRITE_REG(regaddr,regValue);
}

bool rf69_get_flag(struct spi_device *spi, enum flag flag)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "get: flag");
	#endif

	switch(flag) {
	case modeSwitchCompleted:     return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_MODE_READY);
	case readyToReceive:	      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_RX_READY);
	case readyToSend:	      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_TX_READY);
	case pllLocked:		      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_PLL_LOCK);
	case rssiExceededThreshold:   return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_RSSI);
	case timeout:		      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_TIMEOUT);
	case automode:		      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_AUTOMODE);
	case syncAddressMatch:	      return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH);
	case fifoFull:		      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_FULL);
/*	case fifoNotEmpty:	      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_NOT_EMPTY); */
	case fifoEmpty:		      return !(READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_NOT_EMPTY);
	case fifoLevelBelowThreshold: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_LEVEL);
	case fifoOverrun:	      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_OVERRUN);
	case packetSent:	      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_PACKET_SENT);
	case payloadReady:	      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_PAYLOAD_READY);
	case crcOk:		      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_CRC_OK);
	case batteryLow:	      return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_LOW_BAT);
	default:		      return false;
	}
}

int rf69_reset_flag(struct spi_device *spi, enum flag flag)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "reset: flag");
	#endif

	switch(flag) {
	case rssiExceededThreshold: return WRITE_REG(REG_IRQFLAGS1, MASK_IRQFLAGS1_RSSI);
	case syncAddressMatch:	    return WRITE_REG(REG_IRQFLAGS1, MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH);
	case fifoOverrun:	    return WRITE_REG(REG_IRQFLAGS2, MASK_IRQFLAGS2_FIFO_OVERRUN);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: rssi threshold");
	#endif

	/* no value check needed - u8 exactly matches register size */

	return WRITE_REG(REG_RSSITHRESH, threshold);
}

int rf69_set_rx_start_timeout(struct spi_device *spi, u8 timeout)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: start timeout");
	#endif

	/* no value check needed - u8 exactly matches register size */

	return WRITE_REG(REG_RXTIMEOUT1, timeout);
}

int rf69_set_rssi_timeout(struct spi_device *spi, u8 timeout)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: rssi timeout");
	#endif

	/* no value check needed - u8 exactly matches register size */

	return WRITE_REG(REG_RXTIMEOUT2, timeout);
}

int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength)
{
	int retval;
	u8 msb, lsb;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: preamble length");
	#endif

	/* no value check needed - u16 exactly matches register size */

	/* calculate reg settings */
	msb = (preambleLength&0xff00)   >>  8;
	lsb = (preambleLength&0xff);

	/* transmit to chip */
	retval = WRITE_REG(REG_PREAMBLE_MSB, msb);
	if (retval) return retval;
	retval = WRITE_REG(REG_PREAMBLE_LSB, lsb);

	return retval;
}

int rf69_set_sync_enable(struct spi_device *spi, enum optionOnOff optionOnOff)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: sync enable");
	#endif

	switch(optionOnOff) {
	case optionOn:	return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) |  MASK_SYNC_CONFIG_SYNC_ON) );
	case optionOff:	return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_ON) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifoFillCondition fifoFillCondition)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: fifo fill condition");
	#endif

	switch(fifoFillCondition) {
	case always:		 return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) |  MASK_SYNC_CONFIG_FIFO_FILL_CONDITION) );
	case afterSyncInterrupt: return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_FIFO_FILL_CONDITION) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_sync_size(struct spi_device *spi, u8 syncSize)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: sync size");
	#endif

	// check input value
	if (syncSize > 0x07) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// write value
	return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_SIZE) | (syncSize << 3) );
}

int rf69_set_sync_tolerance(struct spi_device *spi, u8 syncTolerance)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: sync tolerance");
	#endif

	// check input value
	if (syncTolerance > 0x07) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// write value
	return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_SIZE) | syncTolerance);
}

int rf69_set_sync_values(struct spi_device *spi, u8 syncValues[8])
{
	int retval = 0;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: sync values");
	#endif

	retval += WRITE_REG(REG_SYNCVALUE1, syncValues[0]);
	retval += WRITE_REG(REG_SYNCVALUE2, syncValues[1]);
	retval += WRITE_REG(REG_SYNCVALUE3, syncValues[2]);
	retval += WRITE_REG(REG_SYNCVALUE4, syncValues[3]);
	retval += WRITE_REG(REG_SYNCVALUE5, syncValues[4]);
	retval += WRITE_REG(REG_SYNCVALUE6, syncValues[5]);
	retval += WRITE_REG(REG_SYNCVALUE7, syncValues[6]);
	retval += WRITE_REG(REG_SYNCVALUE8, syncValues[7]);

	return retval;
}

int rf69_set_packet_format(struct spi_device *spi, enum packetFormat packetFormat)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: packet format");
	#endif

	switch(packetFormat) {
	case packetLengthVar: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) |  MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE) );
	case packetLengthFix: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_crc_enable(struct spi_device *spi, enum optionOnOff optionOnOff)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: crc enable");
	#endif

	switch(optionOnOff) {
	case optionOn:	return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) |  MASK_PACKETCONFIG1_CRC_ON) );
	case optionOff:	return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_CRC_ON) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_adressFiltering(struct spi_device *spi, enum addressFiltering addressFiltering)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: address filtering");
	#endif

	switch (addressFiltering) {
	case filteringOff:	     return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_OFF) );
	case nodeAddress:	     return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_NODE) );
	case nodeOrBroadcastAddress: return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_payload_length(struct spi_device *spi, u8 payloadLength)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: payload length");
	#endif

	return WRITE_REG(REG_PAYLOAD_LENGTH, payloadLength);
}

u8  rf69_get_payload_length(struct spi_device *spi)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "get: payload length");
	#endif

	return (u8) READ_REG(REG_PAYLOAD_LENGTH);
}

int rf69_set_node_address(struct spi_device *spi, u8 nodeAddress)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: node address");
	#endif

	return WRITE_REG(REG_NODEADRS, nodeAddress);
}

int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcastAddress)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: broadcast address");
	#endif

	return WRITE_REG(REG_BROADCASTADRS, broadcastAddress);
}

int rf69_set_tx_start_condition(struct spi_device *spi, enum txStartCondition txStartCondition)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: start condition");
	#endif

	switch(txStartCondition) {
	case fifoLevel:	   return WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) & ~MASK_FIFO_THRESH_TXSTART) );
	case fifoNotEmpty: return WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) |  MASK_FIFO_THRESH_TXSTART) );
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold)
{
	int retval;

	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: fifo threshold");
	#endif

	// check input value
	if (threshold & 0x80) {
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}

	// write value
	retval = WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) & ~MASK_FIFO_THRESH_VALUE) | threshold);
	if (retval)
		return retval;

	// access the fifo to activate new threshold
	return rf69_read_fifo (spi, (u8*) &retval, 1); // retval used as buffer
}

int rf69_set_dagc(struct spi_device *spi, enum dagc dagc)
{
	#ifdef DEBUG
		dev_dbg(&spi->dev, "set: dagc");
	#endif

	switch(dagc) {
	case normalMode:		 return WRITE_REG(REG_TESTDAGC, DAGC_NORMAL);
	case improve:			 return WRITE_REG(REG_TESTDAGC, DAGC_IMPROVED_LOWBETA0);
	case improve4LowModulationIndex: return WRITE_REG(REG_TESTDAGC, DAGC_IMPROVED_LOWBETA1);
	default:
		dev_dbg(&spi->dev, "set: illegal input param");
		return -EINVAL;
	}
}

/*-------------------------------------------------------------------------*/

int rf69_read_fifo (struct spi_device *spi, u8 *buffer, unsigned int size)
{
	#ifdef DEBUG_FIFO_ACCESS
		int i;
	#endif
	struct spi_transfer transfer;
	u8 local_buffer[FIFO_SIZE + 1];
	int retval;

	if (size > FIFO_SIZE) {
		#ifdef DEBUG
			dev_dbg(&spi->dev, "read fifo: passed in buffer bigger then internal buffer \n");
		#endif
		return -EMSGSIZE;
	}

	/* prepare a bidirectional transfer */
	local_buffer[0] = REG_FIFO;
	memset(&transfer, 0, sizeof(transfer));
  	transfer.tx_buf = local_buffer;
  	transfer.rx_buf = local_buffer;
	transfer.len	= size+1;

	retval = spi_sync_transfer(spi, &transfer, 1);

	#ifdef DEBUG_FIFO_ACCESS
		for (i=0; i<size; i++)
			dev_dbg(&spi->dev, "%d - 0x%x\n", i, local_buffer[i+1]);
	#endif

	memcpy(buffer, &local_buffer[1], size);  // TODO: ohne memcopy w�re sch�ner

	return retval;
}

int rf69_write_fifo(struct spi_device *spi, u8 *buffer, unsigned int size)
{
	#ifdef DEBUG_FIFO_ACCESS
		int i;
	#endif
	char spi_address = REG_FIFO | WRITE_BIT;
	u8 local_buffer[FIFO_SIZE + 1];

	if (size > FIFO_SIZE) {
		#ifdef DEBUG
			dev_dbg(&spi->dev, "read fifo: passed in buffer bigger then internal buffer \n");
		#endif
		return -EMSGSIZE;
	}

	local_buffer[0] = spi_address;
	memcpy(&local_buffer[1], buffer, size);  // TODO: ohne memcopy w�re sch�ner

	#ifdef DEBUG_FIFO_ACCESS
		for (i=0; i<size; i++)
			dev_dbg(&spi->dev, "0x%x\n",buffer[i]);
	#endif

	return spi_write (spi, local_buffer, size + 1);
}

/*-------------------------------------------------------------------------*/

u8 rf69_read_reg(struct spi_device *spi, u8 addr)
{
	int retval;

	retval = spi_w8r8(spi, addr);

	#ifdef DEBUG_VALUES
		if (retval < 0)
			/* should never happen, since we already checked,
			 * that module is connected. Therefore no error
			 * handling, just an optional error message...
			 */
			dev_dbg(&spi->dev, "read 0x%x FAILED\n",
				addr);
		else
			dev_dbg(&spi->dev, "read 0x%x from reg 0x%x\n",
				retval,
				addr);
	#endif

	return retval;
}

int rf69_write_reg(struct spi_device *spi, u8 addr, u8 value)
{
	int retval;
	char buffer[2];

	buffer[0] = addr | WRITE_BIT;
	buffer[1] = value;

	retval = spi_write(spi, &buffer, 2);

	#ifdef DEBUG_VALUES
		if (retval < 0)
			/* should never happen, since we already checked,
			 * that module is connected. Therefore no error
			 * handling, just an optional error message...
			 */
			dev_dbg(&spi->dev, "write 0x%x to 0x%x FAILED\n",
				value,
				addr);
		else
			dev_dbg(&spi->dev, "wrote 0x%x to reg 0x%x\n",
				value,
				addr);
	#endif

	return retval;
}


